<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />
		<title>JS对象_class类</title>
		<style type="text/css">

		</style>
	</head>
	<body>
		-- 通过原型和构造函数实现类的概念，各种策略都有自己的问题，也有相应的妥协，实现继承的代码也显得非常冗长和混乱，故es6新增了class类来解决此问题
		-- 类的背后使用的仍然是原型和构造函数的概念
		-- 类看似像函数，但不存在函数提升
		-- class 声明内部会启用严格模式,class 的所有方法（包括静态方法和实例方法）都是不可枚举的
		-- 函数受函数作用域限制，而类受块作用域限制
	</body>
	<script type="text/JavaScript">
		/* 
	 一、类的定义
	 */ 
	// 1.定义类：类声明、类表达式两种方式
	// 1.1类声明
	class Person {}
	// 类表达式
	const Animal = class {};
	
	// 1.2 类的作用域
	//不存在函数提升特性
	console.log(FunctionExpression); // undefined
	var FunctionExpression = function() {};
	
	
	// 函数受函数作用域限制，而类受块作用域限制
	{
		function FunctionDeclaration() {};
		class ClassDeclaration {}
	}
	console.log(FunctionDeclaration); // FunctionDeclaration() {}
	console.log(ClassDeclaration); // ReferenceError: ClassDeclaration is not defined
	
	// 2.类的构成：类可以包含构造函数方法、实例方法、获取函数、设置函数和静态类方法
	// 空类定义，有效
	class Foo {}
	// 有构造函数的类，有效
	class Bar {
	 constructor() {}
	}
	// 有获取函数的类，有效
	class Baz {
	 get myBaz() {}
	}
	// 有静态方法的类，有效
	class Qux {
	 static myQux() {}
	}
	
	// 2.1类表达式的名称：是可选的，可通过 name 属性取得类表达式名称,但是不能在类表达式作用域外访问这个名称
	const Animal = class AnimalName {
		getName() {
			console.log(Animal.name, AnimalName.name)
			return ''
		}
	};
	
	let cat = new Animal();
	console.log(cat.getName()) //AnimalName AnimalName
	console.log(Animal.name) //AnimalName
	console.log(AnimalName.name) //AnimalName is not defined
	
	/* 二、类构造函数 :constructor
	 -- constructor 关键字是类内部块的构造函数
	 -- 在new创建实例时，会自动调用这个函数，一般用于初始化工作
	 -- constructor构造函数不是必需的，不定义构造函数会自动创建一个空函数
	*/
   // 1 实例化:使用 new 操作符实例化,会执行如下操作:
   // -- 实例化的时候、类构造函数默认会返回this对象，指向该类，如果返回别的就会指向其他对象、原型链中的constructor指向Object，无法通过instanceof检测出实例对象和的关联关系
	//(1) 在内存中创建一个新对象。
	//(2) 这个新对象内部的 [[Prototype]] 指针被赋值为构造函数的 prototype 属性。
	//(3) 构造函数内部的 this 被赋值为这个新对象（即 this 指向新对象）。
	//(4) 执行构造函数内部的代码（给新对象添加属性）。
	//(5) 如果构造函数返回非空对象，则返回该对象；否则，返回刚创建的新对象。
   
   class Animal {
   	constructor(ok) {
   	    if(ok){
   			return {}
   		}
   	}
   }
   
   let a = new Animal();
   let p = new Animal(true); 
   
   console.log(a instanceof Animal); //true
   console.log(p instanceof Animal); //为啥打印 false ?
   
   // 类也可以直接被实例化：
   let p = new class {
   	constructor() {
   		this.name = '诸葛'
   	}
   }
   console.log(p)
   
	/* 三、实例、原型和类成员 */
	// 1. 实例成员
	// -- 通过new调用类标识符时，都会执行类构造函数;
	// -- 每个实例都对应一个唯一的成员对象，所有成员都不会在原型上共享，所以当有引用值的属性时、类不会出现和构造函数一样的问题
	
	class Zoo{
		constructor(arg) {
		    this.name=arg||'旺财';
			this.sayName=()=>{
				console.log(this.name)
			};
			this.colors=['red','back']
		}
	}
	let cat =new Zoo();
	console.log(cat.name)//旺财
	cat.colors[0]='green'
	console.log(cat.colors)//["green", "back"]
	
	let dog=new Zoo('tom');
	console.log(dog.name)//tom
	console.log(dog.colors)//["red", "back"]
	
	
	console.log(cat.name === dog.name); // false 
	console.log(cat.sayName === dog.sayName); // false 
	console.log(cat.colors === dog.colors); // false
	
	// 有上面可以看出、类是不是比之前的构造函数好多了？
	
	
	// 2. 原型方法与访问器
	// -- 在类块中定义的所有内容都会定义在类的原型上
	// --  添加到 this 的所有内容都会存在于不同的实例上
	class Zoo{
		constructor(arg) {
		    this.name=arg||'旺财';
			this.sayName1=()=>{
				console.log(this.name)
			}
			// 这些属性和方法存在于不同的实例上
		};
		sayName2=()=>{
			console.log(this.name)
		}
		// sayName2也存在于不同的实例上
		
		sayName3(){
			console.log(this.name)
		}
		// sayName3存在于Zoo原型上
	}
	// 类定义也支持获取和设置访问器。语法与行为跟普通对象一样：
	class Person { 
	 set name(newName) { 
	 this.name_ = newName; 
	 } 
	 get name() { 
	 return this.name_; 
	 } 
	} 
	let p = new Person(); 
	p.name = 'Jake'; 
	console.log(p.name); // Jake
	
	
	
	// 3. 静态类方法:static 关键字
	// -- 类方法前加关键字static，表示该方法不会被继承，而是直接通过类来调用，实例无法调用
	// -- A类继承B类后，B类可以调用A类的静态方法
	class Zoo{
		static setAge(){
		 return	this.name
		}
	}
	Zoo.setAge()
	let  cat=new Zoo();
	console.log(Zoo.setAge())//Zoo
	console.log(cat.setAge())//cat.setAge is not a function
	
	
	/* 四、类的继承 */
	// 1. 继承基础
	// --a.继承类语法：class 子类 extends 父类 {}
	// --b.继承普通构造函数：class 子类 extends 构造函数 {}
	// --extends 关键字也可以在类表达式中使用：let Bar = class extends Foo {}
	// --静态方法也会被基础
	class People{}
	class Race extends People{}
	
	
	// 2. super关键字：这个比较难理解，刻意整理了一份脑图
	// 请查看：https://www.processon.com/view/link/60f7d5cd5653bb0c960f86cf
	window.location.href='super关键字.png'
	
	// 3.类的 prototype 属性和__proto__属性
	class A {}
	class B extends A {}
	
	B.__proto__ === A // true
	B.prototype.__proto__ === A.prototype // true
	
	
	var p1 = new Point(2, 3);
	var p2 = new ColorPoint(2, 3, 'red');
	
	p2.__proto__ === p1.__proto__ // false
	p2.__proto__.__proto__ === p1.__proto__ // true
	
	</script>
</html>
